package com.le.sarrs.servlet;

import groovy.lang.Closure;
import groovy.servlet.AbstractHttpServlet;
import groovy.servlet.ServletBinding;
import groovy.servlet.ServletCategory;
import groovy.util.GroovyScriptEngine;
import groovy.util.ResourceException;
import groovy.util.ScriptException;
import org.codehaus.groovy.runtime.GroovyCategorySupport;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.regex.Matcher;

public class GroovyServlet extends AbstractHttpServlet {
    private static final long serialVersionUID = 3465486652588342998L;
    /**
     * The script engine executing the Groovy scripts for this servlet
     */
    private GroovyScriptEngine gse;

    /**
     * Initialize the GroovyServlet.
     *
     * @throws ServletException
     *             if this method encountered difficulties
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        // Set up the scripting engine
        gse = createGroovyScriptEngine();

        servletContext.log("Groovy servlet initialized on " + gse + ".");
    }

    @Override
    protected String getScriptUri(HttpServletRequest request) {
        String uri = request.getServletPath();
        String info = request.getPathInfo();
        if (info != null) {
            uri += info;
        }

        /*
         * Enable auto ".groovy" extension replacing here! http://cvs.groovy
         * .codehaus.org/viewrep/groovy/groovy/groovy-core/src/main
         * /groovy/servlet/GroovyServlet.java?r=1.10#l259
         */

        return applyResourceNameMatcher(uri);
    }

    @Override
    protected String applyResourceNameMatcher(String uri) {
        if (resourceNamePattern != null) {// mangle resource name with the
                                          // compiled pattern.
            Matcher matcher = resourceNamePattern.matcher(uri);

            String replaced;
            if (resourceNameReplaceAll) {
                replaced = matcher.replaceAll(resourceNameReplacement);
            } else {
                replaced = matcher.replaceFirst(resourceNameReplacement);
            }
            if (!uri.equals(replaced)) {
                if (verbose) {
                    log("Replaced resource name \"" + uri + "\" with \"" + replaced + "\".");
                }
                return replaced;
            }
        }
        return uri;
    }

    /**
     * Handle web requests to the GroovyServlet
     */
    @SuppressWarnings({ "serial", "rawtypes", "unchecked" })
    @Override
    public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Get the script path from the request - include aware (GROOVY-815)
        final String scriptUri = "/WEB-INF/groovy/index.groovy";

        // Set it to HTML by default
        response.setContentType("text/html; charset=" + encoding);

        // Set up the script context
        final ServletBinding binding = new ServletBinding(request, response, servletContext);
        setVariables(binding);

        // Run the script
        try {
            Closure closure = new Closure(gse) {
                @Override
                public Object call() {
                    try {
                        return ((GroovyScriptEngine) getDelegate()).run(scriptUri, binding);
                    } catch (ResourceException e) {
                        throw new RuntimeException(e);
                    } catch (ScriptException e) {
                        throw new RuntimeException(e);
                    }
                }

            };
            GroovyCategorySupport.use(ServletCategory.class, closure);
        } catch (RuntimeException runtimeException) {
            StringBuilder error = new StringBuilder("GroovyServlet Error: ");
            error.append(" script: '");
            error.append(scriptUri);
            error.append("': ");
            Throwable e = runtimeException.getCause();
            /*
             * Null cause?!
             */
            if (e == null) {
                error.append(" Script processing failed.\n");
                error.append(runtimeException.getMessage());
                if (runtimeException.getStackTrace().length > 0) {
                    error.append(runtimeException.getStackTrace()[0].toString());
                }
                servletContext.log(error.toString());
                System.err.println(error.toString());
                runtimeException.printStackTrace(System.err);
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error.toString());
                return;
            }
            /*
             * Resource not found.
             */
            if (e instanceof ResourceException) {
                error.append(" Script not found, sending 404.");
                servletContext.log(error.toString());
                System.err.println(error.toString());
                response.sendError(HttpServletResponse.SC_NOT_FOUND);
                return;
            }
            /*
             * Other internal error. Perhaps syntax?!
             */
            servletContext.log("An error occurred processing the request", runtimeException);
            error.append(e.getMessage());
            if (e.getStackTrace().length > 0) {
                error.append(e.getStackTrace()[0].toString());
            }
            servletContext.log(e.toString());
            System.err.println(e.toString());
            runtimeException.printStackTrace(System.err);
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.toString());
        }
    }

    /**
     * Hook method to setup the GroovyScriptEngine to use.<br>
     * Subclasses may override this method to provide a custom engine.
     */
    protected GroovyScriptEngine createGroovyScriptEngine() {
        return new GroovyScriptEngine(this);
    }
}
